package com.hitqz.robot.camera.util;

import com.hitqz.robot.camera.dto.CameraParamDTO;
import com.hitqz.robot.camera.dto.PtzCtrlDTO;
import com.hitqz.robot.camera.netty.*;
import com.hitqz.robot.camera.type.PtzCtrlTypeEnum;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.concurrent.*;

@Slf4j
public class PelcodCameraToolkit {

    public static TcpClient tcpClient;
    //获取消息结果
    public static final ConcurrentHashMap<String, CompletableFuture<Boolean>> requestMap = new ConcurrentHashMap<>();

    public static boolean init(String ip,Integer port){
        if (tcpClient == null){
            tcpClient = new TcpClient("pelcod", ip,port, new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel channel) throws Exception {
                    channel.pipeline().addLast("decoder", new PelcodDecoder());
                    channel.pipeline().addLast("encoder", new PelcodEncoder());
                    channel.pipeline().addLast("handler", new PelcodMessageHandler());
                }
            });
            NetworkServiceThreadPool.creatNetworkServerOrClient(()->{
                try {
                    tcpClient.connectAsync();
                } catch (Exception e) {
                    log.error("[pelcod] server bind error", e);
                }
            });
        }
        return true;
    }

    public static boolean ptzCtrl(PtzCtrlDTO dto) {
        int ctrlType = dto.getCtrlType();
        PtzCtrlTypeEnum ptzCtrlTypeEnum = PtzCtrlTypeEnum.fromPelcodCmd(ctrlType);
        if (ptzCtrlTypeEnum == null){
            return true;
        }
        Integer pelcodCmd = ptzCtrlTypeEnum.getPelcodCmd();
        PelcodMessage message = new PelcodMessage(pelcodCmd);
        int flag = dto.getFlag();
        switch (ptzCtrlTypeEnum){
            case TILT_UP,TILT_DOWN,PAN_LEFT,PAN_RIGHT:{
                if (flag==0){
                    tcpClient.sendObject(message);
                }else {
                    PelcodMessage stop = new PelcodMessage(new byte[]{(byte) 0x00, (byte) 0x00}, new byte[]{(byte) 0x00, (byte) 0x00});
                    PelcodCameraToolkit.tcpClient.sendObject(stop);
                }
                break;
            }
            case LIGHT: {
                if (flag==0){
                    tcpClient.sendObject(message);
                    //厂商 宏博亚泰的云台 定义了 预置点99 是补光灯。调用是开，设置是关
                    PelcodMessage usePoint99 = new PelcodMessage(0x00070063);
                    tcpClient.sendObject(usePoint99);
                }else {
                    PelcodMessage stop = new PelcodMessage(new byte[]{(byte) 0x00, (byte) 0x6B}, new byte[]{(byte) 0x00, (byte) 0x00});
                    PelcodCameraToolkit.tcpClient.sendObject(stop);
                    PelcodMessage setPoint99 = new PelcodMessage(0x00030063);
                    tcpClient.sendObject(setPoint99);
                }
                break;
            }
            case WIPER:{
                if (flag==0){
                    tcpClient.sendObject(message);
                }else {
                    PelcodMessage stop = new PelcodMessage(new byte[]{(byte) 0x00, (byte) 0x0B}, new byte[]{(byte) 0x00, (byte) 0x01});
                    PelcodCameraToolkit.tcpClient.sendObject(stop);
                }
                break;
            }
            default: {
                tcpClient.sendObject(message);
                break;
            }
        }
        return true;
    }


    public static boolean setPtzPose(float pan,float tilt){
        try {
            short panValue = (short) (pan * 100);
            int tiltValueInt = (int) (tilt * 100);
            if (tiltValueInt<0){
                tiltValueInt=(-1)*tiltValueInt;
            }else {
                tiltValueInt=36000 - tiltValueInt;
            }
            log.info("[pelcod]set pan:{},tilt:{} raw:{}",pan,tilt,tiltValueInt);
            //还原为无符号整数
            short tiltValueShort = (short) (tiltValueInt & 0xFFFF);
            PelcodMessage message = new PelcodMessage(new byte[]{(byte)0x00,(byte)0x4B},new byte[]{(byte)(panValue>>8 & 0xFF),(byte)(panValue & 0xFF)});
            tcpClient.sendObject(message);
            Thread.sleep(500);
            message = new PelcodMessage(new byte[]{(byte)0x00,(byte)0x4D},new byte[]{(byte)(tiltValueShort>>8 & 0xFF),(byte)(tiltValueShort & 0xFF)});
            tcpClient.sendObject(message);
        }catch (Exception e){
            log.info("[pelcod] set ptz pose error", e.getMessage());
        }
        return true;
    }

    public static float getTilt(){
        byte[] cmd = new byte[]{(byte) 0x00, (byte) 0x53};
        byte[] data = new byte[]{(byte) 0x00, (byte) 0x00};
        PelcodMessage queryTilt = new PelcodMessage(cmd, data);
        PelcodCameraToolkit.tcpClient.sendObject(queryTilt);
        waitForResponse(PelcodCacheUtil.KEY_TILT_VALUE);
        return Optional.ofNullable((Float)PelcodCacheUtil.get(PelcodCacheUtil.KEY_TILT_VALUE)).orElse(0f);
    }

    public static float getPan(){
        byte[] cmd = new byte[]{(byte) 0x00, (byte) 0x51};
        byte[] data = new byte[]{(byte) 0x00, (byte) 0x00};
        PelcodMessage queryPan = new PelcodMessage(cmd, data);
        PelcodCameraToolkit.tcpClient.sendObject(queryPan);
        waitForResponse(PelcodCacheUtil.KEY_PAN_VALUE);
        return Optional.ofNullable((Float)PelcodCacheUtil.get(PelcodCacheUtil.KEY_PAN_VALUE)).orElse(0f);
    }


    /**
     * 将字节数组转为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) { // 将字节转为无符号的十六进制字符串
            String hex = String.format("%02X", bytes[i] & 0xFF);
            hexString.append(hex);
            if (i < bytes.length - 1) {
                hexString.append(" ");
            }
        }
        return hexString.toString();
    }


    public static boolean waitForResponse(String key) {
        CompletableFuture<Boolean> future = new CompletableFuture<>();
        requestMap.put(key, future);
        try {
            return future.get(1, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            log.warn("[pelcod]key {} send timeout",key);
        }
        return false;
    }

    public static void release(String key,boolean result){
        CompletableFuture<Boolean> future = requestMap.remove(key);
        if (future != null) {
            future.complete(result);
            log.debug("[track]key {},release",key);
        }
    }


}
